use hypers::futures_util::StreamExt;
use hypers::hyper::header::ACCEPT;
use hypers::mime;
use hypers::prelude::*;
use hypers::serde_json;
use hypers::tokio::{
    self,
    time::{interval, Duration},
};
use hypers::tracing::info;
use hypers::{Event, Sse};
use std::time::Instant;
use systemstat::{Platform, System};
use tokio_stream::wrappers::IntervalStream;

async fn index(_: Request) -> Text<&'static str> {
    Text::Html(
        r#"
        <script>
        var eventSource = new EventSource('event');
        eventSource.onmessage = function(event) {
            document.write("<div>" + event.data + "</div>");
        }
        </script>
    "#,
    )
}
async fn event(_: Request) -> impl Responder {
    let now = Instant::now();
    Sse::new(
        IntervalStream::new(interval(Duration::from_secs(1)))
            .map(move |_| Event::message(now.elapsed().as_secs().to_string())),
    )
    .keep_alive(Duration::from_secs(5))
}
async fn stats(req: Request) -> Result<Response> {
    let mut res = Response::default();
    if !matches!(req.header::<String>(ACCEPT),Ok(ts) if ts == mime::TEXT_EVENT_STREAM.as_ref()) {
        res.status(400);
        return Ok(res);
    }
    let sys = System::new();
    let resp = Sse::new(
        IntervalStream::new(interval(Duration::from_secs(5))).map(move |_| {
            match sys
                .load_average()
                .and_then(|loadavg| serde_json::to_string(&loadavg).map_err(|e| e.into()))
            {
                Ok(loadavg) => Event::message(loadavg),
                Err(err) => {
                    println!("{err}");
                    Event::retry(30)
                }
            }
        }),
    )
    .keep_alive(Duration::from_secs(15))
    .response(res);
    Ok(resp)
}

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt().init();
    let mut root = Router::default();
    root.get("/", index);
    root.get("/event", event);
    root.get("/stats_index", |_| async { // http://127.0.0.1:7878/stats_index
        Text::Html(include_str!("../index.html"))
    });
    root.get("/stats", stats);
    info!("root = {:#?}", root); // http://127.0.0.1:7878/
    let listener = hypers::TcpListener::bind("127.0.0.1:7878").await?;
    hypers::listen(root, listener).await
}
